package main

import (
	"fmt"
	"golang.org/x/net/ipv4"
	"log"
	"math/rand"
	"net"
	"time"
)

const invite = "INVITE sip:+8618820070075@gd.ims.mnc000.mcc460.3gppnetwork.org SIP/2.0\n" +
	"Via: SIP/2.0/UDP 10.188.38.147:5070;branch=z9hG4bK-524287-1---9ef4ac42df7f5e13\n" +
	"Max-Forwards: 70\n" +
	"Route: <sip:10.188.38.134:5080;lr>\n" +
	"Route: <sip:10.188.38.147:5070;lr;ebscom=001.021.093.-1392414489.17>\n" +
	"Route: <sip:10.188.29.14;lr;ORGDLGID=10731-10;Dpt=8134_6;TRC=ffffffff-ffffffff>\n" +
	"Contact: <sip:10.188.38.147:5070;transport=udp;TRC=ffffffff-ffffffff;Dpt=ee1a-200>;+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";audio\n" +
	"To: <tel:18820070075;phone-context=ims.mnc000.mcc460.3gppnetwork.org>\n" +
	"From: <tel:13802885219;noa=subscriber;srvattri=national;phone-context=+86>;tag=399e937deb.rqoryyotyorux-vqxq\n" +
	"Call-ID: vdKnf8oIRhqXFflH_iOCjQ..\n" +
	"CSeq: 1 INVITE\n" +
	"Session-Expires: 1800;refresher=uac\n" +
	"Min-SE: 90\n" +
	"Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, UPDATE, SUBSCRIBE, REFER, PRACK, REGISTER, INFO, PUBLISH, MESSAGE, NOTIFY\n" +
	"Content-Type: application/sdp\n" +
	"Supported: 100rel, path, precondition, replaces, timer\n" +
	"P-Asserted-Identity: \"+8613802885219\"<sip:+8613802885219@gd.ims.mnc000.mcc460.3gppnetwork.org>\n" +
	"P-Asserted-Identity: \"+8613802885219\"<tel:+8613802885219>\n" +
	"Accept-Contact: *;+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"\n" +
	"P-Access-Network-Info: 3GPP-E-UTRAN;utran-cell-id-3gpp=4600028552A92F05;network-provided;sbc-domain=sbc06.020.000.gd.chinamobile.com;ue-ip=[2409:8809:83B2:2240:8:2F9D:78A1:D523];ue-port=63060\n" +
	"P-Charging-Vector: icid-value=\"gzpsbc6bhw.1b0.3ecb.20181122030210\";orig-ioi=gd.chinamobile.com;term-ioi=gd.chinamobile.com\n" +
	"P-Charging-Function-Addresses: ccf=gd.chinamobile.com\n" +
	"P-Early-Media: supported, gated\n" +
	"P-Visited-Network-ID: \"gzpsbc6bhw.gd.ims.mnc000.mcc460.3gppnetwork.org\"\n" +
	"Session-ID: aaccfecd426534256cbaddc360da30e8\n" +
	"Content-Length: 1152\n" +
	"\n" +
	"v=0\n" +
	"o=- 78070823130415722 0 IN IP4 10.188.27.100\n" +
	"s=SBC call\n" +
	"c=IN IP4 10.188.180.78\n" +
	"t=0 0\n" +
	"m=audio 33118 RTP/AVP 104 110 102 108 105 100 111 106 112 18 8 0\n" +
	"b=AS:146\n" +
	"b=RS:612\n" +
	"b=RR:1837\n" +
	"a=rtpmap:104 AMR-WB/16000\n" +
	"a=fmtp:104 max-red=220;mode-change-capability=2\n" +
	"a=rtpmap:110 AMR-WB/16000\n" +
	"a=fmtp:110 octet-align=1;max-red=220;mode-change-capability=2\n" +
	"a=rtpmap:102 AMR/8000\n" +
	"a=fmtp:102 max-red=220;mode-change-capability=2\n" +
	"a=rtpmap:108 AMR/8000\n" +
	"a=fmtp:108 octet-align=1;max-red=220;mode-change-capability=2\n" +
	"a=rtpmap:105 telephone-event/16000\n" +
	"a=fmtp:105 0-15\n" +
	"a=rtpmap:100 telephone-event/8000\n" +
	"a=fmtp:100 0-15\n" +
	"a=ptime:20\n" +
	"a=maxptime:240\n" +
	"a=sendrecv\n" +
	"a=des:qos mandatory local sendrecv\n" +
	"a=curr:qos local none\n" +
	"a=des:qos optional remote sendrecv\n" +
	"a=curr:qos remote none\n" +
	"a=rtpmap:111 EVS/16000\n" +
	"a=fmtp:111 br=9.6-128;bw=swb;mode-change-capability=2;cmr=0;dtx=0;ch-aw-recv=-1;max-red=0\n" +
	"a=rtpmap:106 EVS/16000\n" +
	"a=fmtp:106 br=5.9-128;bw=wb;mode-change-capability=2;cmr=0;dtx=0;ch-aw-recv=-1;max-red=0\n" +
	"a=rtpmap:112 AMR/8000\n" +
	"a=fmtp:112 mode-change-period=2\n" +
	"a=rtpmap:18 G729/8000\n" +
	"a=fmtp:18 annexb=no\n" +
	"a=rtpmap:8 PCMA/8000\n" +
	"a=rtpmap:0 PCMU/8000\n"

type RawSender struct {
	conn     *ipv4.RawConn
	MsgSent  uint64
	LastTime time.Time
}

func NewRawSender(network, address string) RawSender {
	listener, err := net.ListenPacket(network, address)
	if err != nil {
		log.Fatal("can not create packet listener: ", err)
	}

	rawConn, err := ipv4.NewRawConn(listener)
	if err != nil {
		log.Fatal("can not create raw connection: ", err)
	}

	return RawSender{
		conn:     rawConn,
		MsgSent:  0,
		LastTime: time.Time{},
	}
}

func (sender *RawSender) Send(ipHeader *ipv4.Header, data []byte) uint64 {
	if err := sender.conn.WriteTo(ipHeader, data, nil); err != nil {
		log.Fatal("packet send error: ", err)
	} else {
		sender.MsgSent += 1
	}
	return uint64(20 + len(data))
}

func (sender *RawSender) Close() {
	err := sender.conn.Close()
	if err != nil {
		log.Fatal("connection close error: ", err)
	}
}

// 关于udp分片设计参考:https://blog.csdn.net/qq_41453285/article/details/107236053.以实现不上锁分发.
func main() {
	const payLoadSize = 1472 // 第二个分片是184*8
	const offSet = 185

	source, _ := net.ResolveIPAddr("ip4", "10.153.90.4")
	destination, _ := net.ResolveIPAddr("ip4", "143.198.107.33")
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	sourcePort := r.Uint32()
	// sourcePort := 48163
	destPort := 5060
	sender := NewRawSender("ip4:udp", "10.153.90.4")
	iph := GenerateIP(source.IP, destination.IP, 0)

	// 应用层数据
	application := []byte(invite)
	if len(application) < payLoadSize {
		udp := GenerateUdp(application, len(application), source.IP, destination.IP, uint16(sourcePort), uint16(destPort))
		bytes := sender.Send(iph, udp)
		fmt.Printf("bytes sended:%d \n ", bytes)
	} else {
		// 同一个IP数据包Identification字段一致。
		//当Flags字段为1时，表示进行数据包分片，此时若Fragment offset表示分片偏量，若为0，则表示是第一个数据包分片。
		// Flags字段为0，表示后面没有分片。
		// IP分片将IP首部后面的所有数据进行分割，并不区别UDP或TCP首部，且分割以8字节为单位。因此只有第一个分片含有UDP/TCP首部
		iph.Flags = ipv4.MoreFragments
		iph.FragOff = 0
		for i := 0; i < len(application); {
			targetLength := payLoadSize
			if i+payLoadSize >= len(application) { // 最后一个分片了.
				targetLength = len(application) - i
				iph.Flags = ipv4.DontFragment
			}
			RefreshIPCheckSum(iph)
			if i == 0 {
				udp := GenerateUdp(application[i:i+targetLength], len(application), source.IP, destination.IP, uint16(sourcePort), uint16(destPort))
				i += targetLength
				bytes := sender.Send(iph, udp)
				iph.FragOff += offSet
				fmt.Printf("bytes sended:%d \n ", bytes)
			} else {
				bytes := sender.Send(iph, application[i:i+targetLength])
				iph.FragOff += targetLength >> 3
				i += targetLength
				fmt.Printf("bytes sended:%d \n ", bytes)
			}
		}
	}
}

func GenerateUdpFullHeader(srcIP, dstIP net.IP, srcPort, dstPort uint16) []byte {
	length := 8
	udpHeader := make([]byte, 20)
	/*
		UDP 伪首部
	*/
	udpHeader[0], udpHeader[1], udpHeader[2], udpHeader[3] = srcIP[12], srcIP[13], srcIP[14], srcIP[15]
	udpHeader[4], udpHeader[5], udpHeader[6], udpHeader[7] = dstIP[12], dstIP[13], dstIP[14], srcIP[15]

	udpHeader[8], udpHeader[9] = 0x00, 0x11

	udpHeader[10], udpHeader[11] = byte((length>>8)&0xff), byte(length&0xff)
	/*
		UDP 头部
	*/
	// 源端口
	udpHeader[12], udpHeader[13] = byte((srcPort>>8)&0xff), byte(srcPort&0xff)
	// 目的端口
	udpHeader[14], udpHeader[15] = byte((dstPort>>8)&0xff), byte(dstPort&0xff)
	// UDP 头长度
	udpHeader[16], udpHeader[17] = byte((length>>8)&0xff), byte(length&0xff)
	// 效验和
	udpHeader[18], udpHeader[19] = 0x00, 0x00

	return udpHeader
}

func AppendUdpData(udpHeader, data []byte) []byte {
	length := 8 + len(data)

	udpHeader[10], udpHeader[11] = byte((length>>8)&0xff), byte(length&0xff)
	udpHeader[16], udpHeader[17] = byte((length>>8)&0xff), byte(length&0xff)

	pktData := append(udpHeader, data...)
	return pktData[12:]
}

func GenerateUdp(msgByte []byte, dataTotalLength int, srcIP, dstIP net.IP, srcPort, dstPort uint16) []byte {

	// +8.
	length := dataTotalLength + 8 // len(msgByte) + 8
	udpHeader := make([]byte, 20)
	/*
		UDP 伪首部
	*/
	udpHeader[0], udpHeader[1], udpHeader[2], udpHeader[3] = srcIP[12], srcIP[13], srcIP[14], srcIP[15]
	udpHeader[4], udpHeader[5], udpHeader[6], udpHeader[7] = dstIP[12], dstIP[13], dstIP[14], srcIP[15]

	udpHeader[8], udpHeader[9] = 0x00, 0x11

	// length.
	udpHeader[10], udpHeader[11] = byte((length>>8)&0xff), byte(length&0xff)
	/*
		UDP 头部
	*/
	// 源端口
	udpHeader[12], udpHeader[13] = byte((srcPort>>8)&0xff), byte(srcPort&0xff)
	// 目的端口
	udpHeader[14], udpHeader[15] = byte((dstPort>>8)&0xff), byte(dstPort&0xff)
	// UDP 头长度
	udpHeader[16], udpHeader[17] = byte((length>>8)&0xff), byte(length&0xff)
	// 效验和
	udpHeader[18], udpHeader[19] = 0x00, 0x00

	pktData := append(udpHeader, msgByte...)

	// 以下是正规udp报文必须的.
	// 此处计算有些问题, 此处必须放弃自己计算，改用网卡硬件或内核自己计算即可:（手动计算端口改变了会有错.）.
	// 参考:https://blog.csdn.net/sinat_20184565/article/details/82979778
	//check := checkSum(pktData)
	//pktData[18], pktData[19] = byte(check>>8&0xff), byte(check&0xff)
	//fmt.Printf("checkout sum: %x %x\n", pktData[18], pktData[19])
	// udp头部校验和正确方式参考: https://github.com/insomniacslk/dhcp/blob/465dd6c35f6c983206a5bc21fbfff4819ce21eb0/dhcpv4/nclient4/ipv4.go.

	return pktData[12:]
}

// 默认还是ip,
var id = 14551

func GenerateIP(srcIP, dstIP net.IP, msgLen int) *ipv4.Header {
	/*
		IP 头部
	*/
	ipHeader := &ipv4.Header{
		Version:  ipv4.Version,
		Len:      ipv4.HeaderLen,
		TotalLen: ipv4.HeaderLen + msgLen,
		Flags:    ipv4.DontFragment,
		TTL:      64,
		ID:       id,
		Protocol: 17, // udp = 17.
		Checksum: 0,
		Src:      srcIP,
		Dst:      dstIP,
	}
	id++

	// 以下是正规ip头必须的
	header, err := ipHeader.Marshal()
	if err != nil {
		log.Fatal("ip header marshal error: {}", err)
	}

	ipHeader.Checksum = int(checkSum(header))

	return ipHeader
}

func RefreshIPCheckSum(ipHeader *ipv4.Header) {
	// 以下是正规ip头必须的
	header, err := ipHeader.Marshal()
	if err != nil {
		log.Fatal("ip header marshal error: {}", err)
	}

	ipHeader.Checksum = int(checkSum(header))
}

/**
 */
func checkSum(data []byte) uint16 {
	var (
		sum    uint32
		length int = len(data)
		index  int
	)
	for length > 1 {
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		index += 2
		length -= 2
	}
	if length > 0 {
		sum += uint32(data[index])
	}
	sum += (sum >> 16)

	//
	return uint16(^sum) + 0x7f
}

// 下面是是参考ip check sum来的..
// 参考:https://www.jb51.net/article/126175.htm.
// https://www.opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/.
func checkUdpSum(msg []byte) uint16 {
	sum := 0
	for n := 1; n < len(msg)-1; n += 2 {
		sum += int(msg[n])*256 + int(msg[n+1])
	}
	sum = (sum >> 16) + (sum & 0xffff)
	sum += (sum >> 16)
	var ans = uint16(^sum)
	return ans
}
